using System;
using System.Collections.Generic;
using System.Text;

namespace NativeWiFiLibrary
{
    public class Network
    {
        public string ProfileName { get; set; }
        public string Ssid { get; set; }
        public BssType BssType { get; set; }
        public int NumberOfBssids { get; set; }
        public bool IsNetworkConnectable { get; set; }
        public long NotConnectableReason { get; set; }
        public int NumberOfPhyTypes { get; set; }
        public List<PhyType> PhyTypes { get; set; }
        public bool HasMorePhyTypes { get; set; }
        public int SignalQuality { get; set; }
        public bool IsSecurityEnabled { get; set; }
        public AuthenticationAlgorithm DefaultAuthenticationAlgorithm { get; set; }
        public CipherAlgorithm DefaultCipherAlgorithm { get; set; }
        public bool IsConnected { get; set; }
        public bool HasProfile { get; set; }

        private Guid _deviceGuid;

        /// <summary>
        /// Hide constructor
        /// </summary>
        private Network() { }

        #region Read-only properties
        public string DefaultAuthenticationAlgorithmString
        {
            get
            {
                return WlanConv.GetAuthentication(this.DefaultAuthenticationAlgorithm);
            }
        }
        public string DefaultCipherAlgorithmString
        {
            get
            {
                return WlanConv.GetEncryption(this.DefaultCipherAlgorithm);
            }
        }
        #endregion

        #region Constructors
        internal static Network FromWlanApi(Guid deviceGuid, WlanApi.WlanAvailableNetwork original)
        {
            Network network = new Network
                {
                    _deviceGuid = deviceGuid,
                    BssType = (BssType)original.dot11BssType,
                    DefaultAuthenticationAlgorithm = (AuthenticationAlgorithm)original.dot11DefaultAuthAlgorithm,
                    DefaultCipherAlgorithm = (CipherAlgorithm)original.dot11DefaultCipherAlgorithm,
                    HasMorePhyTypes = original.bMorePhyTypes != 0,
                    IsNetworkConnectable = original.bNetworkConnectable != 0,
                    IsSecurityEnabled = original.bSecurityEnabled != 0,
                    IsConnected = (original.dwFlags & WlanApi.WLAN_AVAILABLE_NETWORK_CONNECTED) != 0,
                    HasProfile = (original.dwFlags & WlanApi.WLAN_AVAILABLE_NETWORK_HAS_PROFILE) != 0,
                    NotConnectableReason = original.wlanNotConnectableReason,
                    NumberOfBssids = original.uNumberOfBssids,
                    NumberOfPhyTypes = original.uNumberOfPhyTypes,
                    ProfileName = original.strProfileName,
                    SignalQuality = original.wlanSignalQuality,
                    Ssid = original.dot11Ssid.SSID
                };

            network.PhyTypes = new List<PhyType>();
            foreach (WlanApi.DOT11_PHY_TYPE type in original.dot11PhyTypes)
                if (type != WlanApi.DOT11_PHY_TYPE.dot11_phy_type_unknown && type != WlanApi.DOT11_PHY_TYPE.dot11_phy_type_any)
                    network.PhyTypes.Add((PhyType)type);

            return network;
        }
        #endregion

        /// <summary>
        /// Returns a list of access points for this network.
        /// </summary>
        /// <returns></returns>
        public List<AccessPoint> GetAccessPoints()
        {
            using (WlanManager wlanManager = new WlanManager())
            {
                List<AccessPoint> accessPoints = wlanManager.GetAccessPoints(_deviceGuid, Ssid, BssType, IsSecurityEnabled);
                accessPoints.ForEach(ap => ap.Network = this);
                return accessPoints;
            }
        }

        public string GetProfile()
        {
            if (this.HasProfile)
            {
                using (WlanManager wlanManager = new WlanManager())
                {
                    return wlanManager.GetProfile(this._deviceGuid, this.ProfileName);
                }
            }
            else
            {
                throw new InvalidOperationException("This network does not have a profile.");
            }
        }

        /// <summary>
        /// Connects to this network.
        /// </summary>
        public void Connect()
        {
            using (WlanManager wlanManager = new WlanManager())
            {
                if (this.HasProfile)
                {
                    wlanManager.ConnectByProfile(this._deviceGuid, this.ProfileName);
                }
                else
                {
                    wlanManager.Connect(this._deviceGuid, this.Ssid, this.BssType, this.DefaultAuthenticationAlgorithm, this.DefaultCipherAlgorithm);
                }
            }
        }

        public void DeleteProfile()
        {
            if (this.HasProfile)
            {
                using (WlanManager wlanManager = new WlanManager())
                {
                    wlanManager.WlanDeleteProfile(this._deviceGuid, this.ProfileName);
                }
            }
            else
            {
                throw new InvalidOperationException("This network does not have a profile.");
            }
        }

        public void EditProfile()
        {
            if (this.HasProfile)
            {
                WlanManager.WlanUIEditProfile(this._deviceGuid, this.ProfileName);
            }
            else
            {
                throw new InvalidOperationException("This network does not have a profile.");
            }
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            Network other = obj as Network;
            if (other != null)
            {
                return this._deviceGuid == other._deviceGuid &&
                       this.BssType == other.BssType &&
                       this.DefaultAuthenticationAlgorithm == other.DefaultAuthenticationAlgorithm &&
                       this.DefaultCipherAlgorithm == other.DefaultCipherAlgorithm &&
                       this.IsNetworkConnectable == other.IsNetworkConnectable &&
                       this.IsSecurityEnabled == other.IsSecurityEnabled &&
                       this.Ssid == other.Ssid;
            }
            else
            {
                return base.Equals(obj);
            }
        }
    }
}